﻿using System;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Dsl.Ped.Common.Converters;
using Dsl.Ped.Framework.Common;
using Dsl.Ped.Controls.SyntaxHighlighter;
using Dsl.Ped.Framework.Extensions;
using Dsl.Ped.Framework.Projections.TextProjections;

namespace Dsl.Ped.Framework.Projections
{
    /// <summary>
    /// Factory for the creation of text-based projection
    /// </summary>
    public class TextProjectionFactory : ProjectionFactoryBase
    {
        /// <summary>
        /// Initializes properly text-based projection
        /// </summary>
        /// <typeparam name="T">Type of the projection (TextBox, Label, etc.)</typeparam>
        /// <param name="projectionHint">Hint for the creation of a projection</param>
        /// <param name="source">Source object of the projection</param>
        /// <param name="type">Type of a property which the projection is bound to</param>
        /// <param name="bindingPath">Path to the property of an object represented in a projection</param>
        /// <returns><see cref="FrameworkElement"/> representing text-based projection</returns>
        public static FrameworkElement InitializeTextProjection<T>(
            ITextProjectionHint<T> projectionHint, object source, Type type, string bindingPath = "Value")
            where T : FrameworkElement, new()
        {
            var projection = InitializeProjection<T>(projectionHint, type);

            var defaultFrameworkElementConfiguration = GetDefaultFrameworkElementConfiguration<T>();
            if (defaultFrameworkElementConfiguration != null)
            {
                defaultFrameworkElementConfiguration(projection);
            }

            if (projectionHint.TextProperty != null)
            {
                var binding = new Binding(bindingPath)
                                  {
                                      Source = source,
                                      Mode = BindingMode.OneWay,
                                      StringFormat = null,
                                      ValidatesOnDataErrors = true,
                                      NotifyOnValidationError = true,
                                      Converter = new ToStringConverter(projectionHint.ToStringConverter),
                                      UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                                  };
                projection.SetBinding(projectionHint.TextProperty, binding);
            }

            return projection;
        }

        #region SyntaxHighlighter

        /// <summary>
        /// Get the projection, where the source object's property is bound to a <see cref="RichTextBox"/>
        /// and converted to string. KeyWords will be marked significantly.
        /// </summary>
        /// <param name="source">Source object</param>
        /// <param name="propertyType">Type of the property</param>
        /// <param name="propertyName">Name of the property</param>
        /// <param name="toStringConverter">Converter of the property value to string</param>
        /// <param name="customConfiguration">Custom configuration that can be applied on the <see cref="SyntaxHighlighterControl"/></param>
        /// <returns><see cref="IProjection"/> configured for <see cref="SyntaxHighlighterControl"/> and a source object</returns>
        public static IProjection GetSyntaxHighlighterProjection(object source, Type propertyType, string propertyName, Func<object, string> toStringConverter, Action<SyntaxHighlighterControl> customConfiguration = null)
        {
            if (source == null)
            {
                return null;
            }

            var projectionHint = new TextProjectionHint<SyntaxHighlighterControl>(
                SyntaxHighlighterControl.TextProperty,
                (syntaxHighlighterControl, type) => { syntaxHighlighterControl.IsReadOnly = true; }, 
                customConfiguration,
                toStringConverter);

            return new Projection() { View = InitializeTextProjection(projectionHint, source, propertyType, propertyName)};
        }

        /// <summary>
        /// Get projection in which the bound object is transformed to string via <see cref="toStringConverter"/> 
        /// and finally highlighted 
        /// </summary>
        /// <param name="source">Source object</param>
        /// <param name="toStringConverter">Converter of the object to a string</param>
        /// <param name="customConfiguration">Custom configuration that can be applied on the projection in a form of <see cref="SyntaxHighlighterControl"/></param>
        /// <returns>Projection of a given object in a <see cref="SyntaxHighlighterControl"/></returns>
        public static IProjection GetSyntaxHighlighterProjection(
            object source, Func<object, string> toStringConverter, Action<SyntaxHighlighterControl> customConfiguration = null)
        {
            if (source == null) return null;

            return GetSyntaxHighlighterProjection(source, source.GetType(), "", toStringConverter, customConfiguration);
        }

        /// <summary>
        /// Gets the projeciton in which the bound object is transformed to string via <see cref="toStringConverter"/>
        /// and finally highlighted
        /// </summary>
        /// <typeparam name="T">Type of the projection</typeparam>
        /// <param name="source">Source object</param>
        /// <param name="exprMemberExpression">Member that leads to a specific property of a <see cref="source"/></param>
        /// <param name="toStringConverter">Converter of the object to a string</param>
        /// <param name="customConfiguration">Custom configuration applied to the <see cref="SyntaxHighlighterControl"/></param>
        /// <returns>SyntaxHighlighter projection</returns>
        public static IProjection GetSyntaxHighlighterProjection<T>(object source, Expression<Func<T>> exprMemberExpression, Func<object, string> toStringConverter, Action<SyntaxHighlighterControl> customConfiguration = null)
        {
            var member = exprMemberExpression.GetMember();
            return GetSyntaxHighlighterProjection(source, member.Type, member.Name, toStringConverter, customConfiguration);
        }

        #endregion
    }
}
